package one.beex.robot;

import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import io.broker.api.client.domain.account.BeeOrder;
import io.broker.api.client.domain.account.NewOrderResponse;
import io.broker.api.client.domain.account.Order;
import io.broker.api.client.domain.account.OrderSide;
import io.broker.api.client.service.BrokerApiCustomService;
import one.beex.robot.client.RobotClient;
import one.beex.robot.client.RobotConfig;
import one.beex.robot.log.LoggerUtil;
import one.beex.robot.symbol.SymbolInfo;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@RestController
public class RobotApiFacade {

    private ScheduledExecutorService monitorOrderExecutor = new ScheduledThreadPoolExecutor(2);

    private Set<ScheduledFuture> scheduledFutures = Sets.newHashSet();

    private BrokerApiCustomService brokerApiCustomService;

    private Map<String, Object> response = Maps.newHashMap();

    @GetMapping("/robot/task.sell")
    public String doTask(
            String token,
            String cookie,
            String accountId,
            String symbol,
            BigDecimal inputPrice,
            BigDecimal inputVolume) {
        BrokerApiCustomService brokerApiCustomService =
                new BrokerApiCustomService(token, cookie, accountId);
        monitorOrderExecutor.scheduleAtFixedRate(
                () -> {
                    try {
                        NewOrderResponse newOrderResponse =
                                brokerApiCustomService.newOrder(
                                        symbol, OrderSide.SELL, inputVolume, inputPrice);
                        brokerApiCustomService.cancelOrder(
                                newOrderResponse.getClientOrderId(),
                                newOrderResponse.getOrderId() + "");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                },
                0,
                1,
                TimeUnit.SECONDS);

        return "SUCCESS";
    }

    @GetMapping("/robot/task.1")
    public String doTask1(
            String token,
            String cookie,
            String accountId,
            String symbol,
            BigDecimal inputPrice,
            BigDecimal inputVolume) {
        BrokerApiCustomService brokerApiCustomService =
                new BrokerApiCustomService(token, cookie, accountId);
        monitorOrderExecutor.scheduleAtFixedRate(
                () -> {
                    try {
                        brokerApiCustomService.newOrder(
                                symbol, OrderSide.SELL, inputVolume, inputPrice);
                        brokerApiCustomService.newOrder(
                                symbol, OrderSide.BUY, inputVolume, inputPrice);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                },
                0,
                1,
                TimeUnit.SECONDS);

        return "SUCCESS";
    }

    @GetMapping("/robot/task.add")
    public Map<String, Object> doTask(
            String token,
            String cookie,
            String accountId,
            String symbol,
            BigDecimal times,
            BigDecimal volume,
            BigDecimal inputVolume,
            BigDecimal inputPrice,
            String end_trade_id,
            Long targetUsdt) {

        RobotConfig robotConfig =
                new RobotConfig().setTimesBetween7min(times).setVolumeBetween7min(volume);

        SymbolInfo symbolInfo =
                new SymbolInfo()
                        .setSymbol(symbol)
                        .setMinQuantity(new BigDecimal("1"))
                        .setMinPrice(new BigDecimal("0.0001"));

        brokerApiCustomService = new BrokerApiCustomService(token, cookie, accountId);

        LoggerUtil.info("open order({})", brokerApiCustomService.getOpenOrders());

        List<Order> openOrders;

        if (!CollectionUtils.isEmpty(openOrders = brokerApiCustomService.getOpenOrders())) {

            Map<String, Object> openOrdersMap = Maps.newHashMap();
            openOrdersMap.put("openOrders", openOrders);

            return openOrdersMap;
        }

        new Thread(
                        new RobotClient(
                                symbol,
                                inputPrice,
                                inputVolume,
                                robotConfig,
                                brokerApiCustomService,
                                symbolInfo))
                .start();

        response.put("targetUsdt", BigDecimal.valueOf(targetUsdt));

        if (Strings.isNullOrEmpty(end_trade_id)) {
            end_trade_id = "0";
        }
        final String finalFrom_trade_id = end_trade_id;

        scheduledFutures.add(
                monitorOrderExecutor.scheduleAtFixedRate(
                        () -> monitorCost(finalFrom_trade_id),
                        0,
                        1000 * 15,
                        TimeUnit.MILLISECONDS));

        Map<String, Object> response = Maps.newHashMap();
        response.put("status", "success");

        return response;
    }

    @GetMapping("/robot/task.destroy")
    public Map<String, Object> doTask() {
        destroyRobot();
        return response;
    }

    private void destroyRobot() {
        // 撤销所有订单
        List<Order> openOrders = brokerApiCustomService.getOpenOrders();

        if (!CollectionUtils.isEmpty(openOrders)) {
            for (Order order : openOrders) {
                brokerApiCustomService.cancelOrder(
                        order.getClientOrderId(), order.getOrderId().toString());
            }
        }

        for (ScheduledFuture scheduledFuture : scheduledFutures) {
            scheduledFuture.cancel(true);
        }

        scheduledFutures.clear();

        LoggerUtil.info("robot destroy");
    }

    private void monitorCost(String end_trade_id) {
        try {

            List<BeeOrder> beeOrders = brokerApiCustomService.queryBeeOrders(end_trade_id);

            BigDecimal usdtCount = BigDecimal.ZERO;

            if (!CollectionUtils.isEmpty(beeOrders)) {

                for (BeeOrder beeOrder : beeOrders) {
                    if ("USDT".equals(beeOrder.getFeeTokenId())) {
                        usdtCount = usdtCount.add(beeOrder.getFee());
                    } else {
                        usdtCount =
                                usdtCount.add(
                                        beeOrder.getFee()
                                                .multiply(new BigDecimal(beeOrder.getPrice())));
                    }
                }

                response.put("orderCount", beeOrders.size());
            }

            response.put("usdtCount", usdtCount);

            BigDecimal targetUsdt = (BigDecimal) response.get("targetUsdt");

            if (usdtCount.compareTo(targetUsdt) >= 0) {
                destroyRobot();
            }

            LoggerUtil.info("cost({})", response);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @GetMapping("/robot/cost.query")
    public Map<String, Object> cost() {
        return response;
    }
}
